/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package embeddedpaint.model.layers;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

public class LayerManager extends Observable implements Observer
{
    public static final String  G_UPDATE_KEY_ADD_LAYER          = "ADD_LAYER";
    public static final String  G_UPDATE_KEY_LAYER_CHANGING     = "LAYER_CHANGING";
    public static final Color   M_DEFAULT_BACKGROUND_COLOR      = Color.WHITE;
    public static final int     G_BORDER_SIZE                   = 1;
    
    private static final int    M_DEFAULT_SIZE_X                = 500;
    private static final int    M_DEFAULT_SIZE_Y                = 500;
    private static final int    M_TOP_LEFT_POINT_X              = 0;
    private static final int    M_TOP_LEFT_POINT_Y              = 0;
    private static final int    M_NUMBER_OF_HORIZONTAL_BORDER   = 2;
    private static final int    M_NUMBER_OF_VERTICAL_BORDER     = 2;
    private static final int    M_DEFAULT_IMAGE_TYPE            = ALayer.M_DEFAULT_IMAGE_TYPE;
    private static final Color  M_DEFAULT_BORDER_COLOR          = Color.BLACK;
    private static final String M_DEFAULT_NAME_MERGED_LAYER     = "Mixed Layer";
    
    private ArrayList<ALayer>   m_imageList;
    private ArrayList<ALayer>   m_selectedImage;
    private int                 m_width;
    private int                 m_height;
    private Color               m_backroundColor;

    public LayerManager ()
    {
        this.m_imageList        = new ArrayList<>();
        this.m_selectedImage    = new ArrayList<>();

        this.m_backroundColor   = M_DEFAULT_BACKGROUND_COLOR;
        
        this.resize(M_DEFAULT_SIZE_X, M_DEFAULT_SIZE_Y);
    }

    public void add (ALayer e)
    {
        this.m_imageList.add(e);
        
        e.addObserver(this);
        
        this.updateObservers();
    }

    public void remove (Object o)
    {
        this.m_selectedImage.remove(o);
        this.m_imageList    .remove(o);
        
        this.updateObservers();
    }

    public final void resize (int width, int height)
    {
        this.m_height   = height;
        this.m_width    = width;

        this.update(null, null);
    }

    public ALayer get (int index)
    {
        return m_imageList.get(index);
    }

    public int getM_width ()
    {
        return m_width;
    }

    public int getM_height ()
    {
        return m_height;
    }

    public int getM_widthWithBorder ()
    {
        return m_width + G_BORDER_SIZE * M_NUMBER_OF_HORIZONTAL_BORDER;
    }

    public int getM_heightWithBorder ()
    {
        return m_height + G_BORDER_SIZE * M_NUMBER_OF_VERTICAL_BORDER;
    }

    public ArrayList<ALayer> getM_imageList ()
    {
        return m_imageList;
    }

    public ArrayList<ALayer> getM_selectedImage ()
    {
        return m_selectedImage;
    }

    public void setM_selectedImage (ArrayList<ALayer> m_selectedImage)
    {
        this.m_selectedImage = m_selectedImage;
    }
    
    public void updateObservers ()
    {
        this.setChanged();
        this.notifyObservers(G_UPDATE_KEY_ADD_LAYER);
    }

    @Override
    public void update (Observable o, Object arg)
    {
        this.setChanged();
        this.notifyObservers(G_UPDATE_KEY_LAYER_CHANGING);
    }

    public void move (int lastPosition, int newPosition)
    {
        ALayer layer = this.m_imageList.get(lastPosition);
        
        this.m_imageList.remove (layer);
        this.m_imageList.add    (newPosition, layer);

        this.updateObservers();
    }

    public void setM_width (int m_width)
    {
        this.m_width = m_width;
        this.updateObservers();
    }

    public void setM_height (int m_height)
    {
        this.m_height = m_height;
        this.updateObservers();
    }

    public void drawMe (Graphics2D g2d)
    {
        g2d.clipRect(M_TOP_LEFT_POINT_X, M_TOP_LEFT_POINT_Y, m_width, m_height);

        if (this.m_backroundColor != null)
        {
            g2d.setColor(this.m_backroundColor);
            g2d.fillRect(M_TOP_LEFT_POINT_X, M_TOP_LEFT_POINT_Y, m_width, m_height);
        }

        for (ALayer img : this.m_imageList)
        {
            img.drawMe(g2d);
        }

    }

    public void drawMeWithDelimitedBorder (Graphics2D g2d)
    {
        Image inside = new BufferedImage(m_width + G_BORDER_SIZE, m_height + G_BORDER_SIZE, M_DEFAULT_IMAGE_TYPE);
        
        Graphics2D g2dInside = (Graphics2D) inside.getGraphics();

        this.drawMe(g2dInside);

        g2dInside.dispose();

        inside.flush();

        g2d.setColor(M_DEFAULT_BORDER_COLOR);
        g2d.drawImage(inside, G_BORDER_SIZE, G_BORDER_SIZE, m_width, m_height, null);
        g2d.drawRect(M_TOP_LEFT_POINT_X, M_TOP_LEFT_POINT_Y, m_width, m_height);
    }

    public Color getM_backroundColor ()
    {
        return m_backroundColor;
    }

    public void setM_backroundColor(Color m_backroundColor)
    {
        this.m_backroundColor = m_backroundColor;
        
        this.updateObservers();
    }

    public void layerTextToLayerImage (LayerText layer)
    {
        ALayer  newLayerImage   = layer.getLayerImage();
        int     position        = this.m_imageList.indexOf(layer);
        
        newLayerImage.addObserver(this);

        this.m_imageList.remove (position);
        this.m_imageList.add    (position, newLayerImage);

        this.updateObservers();
    }

    public boolean selectedLayerIsOnlyLayerImage ()
    {
        for (ALayer img : this.m_selectedImage)
        {
            if (!(img instanceof LayerImage))
            {
                return false;
            }
        }
        return true;
    }

    public void mergeSelectedLayers ()
    {
        Image   image       = new BufferedImage(m_width, m_height, M_DEFAULT_IMAGE_TYPE);
        int     position    = Integer.MAX_VALUE;

        Graphics2D g2d = (Graphics2D) image.getGraphics();

        for (ALayer aLayer : m_selectedImage)
        {
            position = Math.min(position, m_imageList.indexOf(aLayer));
            
            m_imageList.remove(aLayer);
            
            aLayer.drawMe(g2d);
        }
        
        g2d.dispose();

        LayerImage layer = new LayerImage(M_DEFAULT_NAME_MERGED_LAYER);
        layer.setM_height(m_height);
        layer.setM_width (m_width);
        layer.setM_img   (image);
        layer.addObserver(this);
        
        m_imageList.add(position, layer);

        m_selectedImage.clear();

        this.updateObservers();
    }
}
